use std::cell::RefCell;
use std::rc::Rc;

use crate::instruction::instruction::Instruction;
use crate::vm_thread::{VMFrame, VMThread};
use crate::vm_val::{VMValByte, VMValChar, VMValDouble, VMValEnum, VMValFloat, VMValInteger, VMValLong, VMValShort};

pub struct i2b {}

impl Instruction for i2b {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let int_val = VMValEnum::convert2integer(&val);
        rw_operand_stack.push(VMValEnum::VMValByte { val: VMValByte { num: int_val.num as i8 } });
    }

    fn to_string(&self) -> String {
        String::from("i2b")
    }
}

pub struct i2c {}

impl Instruction for i2c {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let int_val = VMValEnum::convert2integer(&val);
        rw_operand_stack.push(VMValEnum::VMValChar { val: VMValChar { val: int_val.num as u16 } });
    }

    fn to_string(&self) -> String {
        String::from("i2c")
    }
}


pub struct i2d {}

impl Instruction for i2d {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let int_val = VMValEnum::convert2integer(&val);
        rw_operand_stack.push(VMValEnum::VMValDouble { val: VMValDouble { num: int_val.num as f64 } });
    }

    fn to_string(&self) -> String {
        String::from("i2d")
    }
}


pub struct i2f {}

impl Instruction for i2f {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let int_val = VMValEnum::convert2integer(&val);
        rw_operand_stack.push(VMValEnum::VMValFloat { val: VMValFloat { num: int_val.num as f32 } });
    }

    fn to_string(&self) -> String {
        String::from("i2f")
    }
}

pub struct i2l {}

impl Instruction for i2l {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let int_val = VMValEnum::convert2integer(&val);
        rw_operand_stack.push(VMValEnum::VMValLong { val: VMValLong { num: int_val.num as i64 } });
    }

    fn to_string(&self) -> String {
        String::from("i2l")
    }
}


pub struct i2s {}

impl Instruction for i2s {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let int_val = VMValEnum::convert2integer(&val);
        rw_operand_stack.push(VMValEnum::VMValShort { val: VMValShort { num: int_val.num as i16 } });
    }

    fn to_string(&self) -> String {
        String::from("i2s")
    }
}


pub struct d2f {}

impl Instruction for d2f {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let double_val = VMValEnum::convert2double(&val);
        rw_operand_stack.push(VMValEnum::VMValFloat { val: VMValFloat { num: double_val.num as f32 } });
    }

    fn to_string(&self) -> String {
        String::from("d2f")
    }
}


pub struct d2i {}

impl Instruction for d2i {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let double_val = VMValEnum::convert2double(&val);
        rw_operand_stack.push(VMValEnum::VMValInteger { val: VMValInteger { num: double_val.num as i32 } });
    }

    fn to_string(&self) -> String {
        String::from("d2i")
    }
}


pub struct d2l {}

impl Instruction for d2l {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow_mut();
        let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let double_val = VMValEnum::convert2double(&val);
        rw_operand_stack.push(VMValEnum::VMValLong { val: VMValLong { num: double_val.num as i64 } });
    }

    fn to_string(&self) -> String {
        String::from("d2l")
    }
}

